package com.panghl.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.gson.Gson;
import com.panghl.config.WxPayConfig;
import com.panghl.entity.OrderInfo;
import com.panghl.entity.Product;
import com.panghl.enums.OrderStatus;
import com.panghl.mapper.OrderInfoMapper;
import com.panghl.mapper.ProductMapper;
import com.panghl.service.OrderInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.panghl.service.PaymentInfoService;
import com.panghl.util.OrderNoUtils;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Resource
    private ProductMapper productMapper;
    @Resource
    private PaymentInfoService paymentInfoService;

    @Resource
    private OrderInfoMapper orderInfoMapper;

    @Resource
    private WxPayConfig wxPayConfig;

    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public OrderInfo createOrderByProductId(Long productId,String payType) {

        //查找已存在但未支付的订单
        OrderInfo noPayOrder = this.getNoPayOrderByProductId(productId);
        if (null != noPayOrder) {
            return noPayOrder;
        }

        // 获取商品信息
        Product product = productMapper.selectById(productId);

        log.info("生成订单...");
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setTitle(product.getTitle());
        orderInfo.setOrderNo(OrderNoUtils.getOrderNo());
        orderInfo.setProductId(productId);
        orderInfo.setTotalFee(product.getPrice());//单位 分
        orderInfo.setOrderStatus(OrderStatus.NOTPAY.getType());
        orderInfo.setPaymentType(payType);
        baseMapper.insert(orderInfo);
        return orderInfo;
    }

    @Override
    public void saveCodeUrl(String orderNo, String codeUrl) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setCodeUrl(codeUrl);
        baseMapper.update(orderInfo, new QueryWrapper<OrderInfo>().lambda().eq(OrderInfo::getOrderNo, orderNo));
    }

    @Override
    public List<OrderInfo> listOrderByCreateTimeDesc() {
        return lambdaQuery().orderByDesc(OrderInfo::getCreateTime).list();
    }

    @Override
    public void processOrder(Map<String, Object> wxBodyMap) throws GeneralSecurityException {
        log.info("处理订单");
        //解密报文
        String plaintText = decryptFromResource(wxBodyMap);
        Gson gson = new Gson();
        // 将明文转换成map
        Map<String, Object> plaintTextMap = gson.fromJson(plaintText, Map.class);
        String orderNo = (String) plaintTextMap.get("out_trade_no");

        /**
         * 在对业务数据进行状态检查和处理之前，
         * 要采用数据锁进行并发控制，
         * 以避免函数重入造成的数据混乱
         */
        // 尝试获取锁： 成功获取则立即返回true，获取失败则立即返回false。不必一直等待锁的释放
        if (lock.tryLock()) {
            try {
                // 处理重复的通知
                // 接口调用的幂等性：无论接口被调用多少次，产生的结果是一致的；
                String orderStatus = getOrderStatus(orderNo);
                if (!OrderStatus.NOTPAY.getType().equals(orderStatus)) {
                    log.info("订单状态是未支付....");
                    return;
                }

                // 模拟通知并发 ---> 采用数据锁解决
                //try {
                //    log.info("睡眠5s");
                //    TimeUnit.SECONDS.sleep(5);
                //} catch (InterruptedException e) {
                //    e.printStackTrace();
                //}

                // 更新订单状态
                updateStatusByOrderNo(orderNo, OrderStatus.SUCCESS);

                // 记录支付日志
                paymentInfoService.createPaymentInfo(plaintTextMap);
            } finally {
                //释放锁
                lock.unlock();
            }
        }

    }

    @Override
    public String getOrderStatus(String orderNo) {
        log.info("获取订单状态--->{}",orderNo);
        OrderInfo orderInfo = baseMapper.selectOne(new QueryWrapper<OrderInfo>().lambda().eq(OrderInfo::getOrderNo, orderNo).last("limit 1"));
        if (null == orderInfo) {
            return "";
        }
        return orderInfo.getOrderStatus();
    }

    @Override
    public void updateStatusByOrderNo(String orderNo, OrderStatus orderStatus) {
        log.info("更新订单状态---->{}", orderStatus.getType());
        update(new UpdateWrapper<OrderInfo>().lambda().set(OrderInfo::getOrderStatus, orderStatus.getType()).eq(OrderInfo::getOrderNo, orderNo));
    }

    /**
     * 查询创建超过minutes分钟未支付的订单
     * @param minutes
     * @param payType
     * @return
     */
    @Override
    public List<OrderInfo> getNoPayOrderByDuration(int minutes, String payType) {
        Instant instant = Instant.now().minus(Duration.ofMinutes(minutes));
        return lambdaQuery().eq(OrderInfo::getOrderStatus,OrderStatus.NOTPAY.getType()).
                eq(OrderInfo::getPaymentType,payType)
                .le(OrderInfo::getCreateTime,instant).list();
    }



    @Override
    public OrderInfo getOrderByOrderNo(String orderNo) {
        return lambdaQuery().eq(OrderInfo::getOrderNo,orderNo).last(" limit 1").one();
    }

    /**
     * 对称解密
     *
     * @param wxBodyMap
     * @return
     */
    private String decryptFromResource(Map<String, Object> wxBodyMap) throws GeneralSecurityException {
        log.info("报文解密");
        //通知数据
        Map<String, String> resourceMap = (Map) wxBodyMap.get("resource");
        //数据密文
        String ciphertext = resourceMap.get("ciphertext");
        log.info("密文====>{}", ciphertext);
        //随机串
        String nonce = resourceMap.get("nonce");
        //附加数据
        String associatedData = resourceMap.get("associated_data");

        AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        //明文
        String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                ciphertext);
        log.info("明文====>{}", plainText);
        return plainText;
    }

    /**
     * 查询某用户对某商品未支付的订单
     *
     * @param productId
     * @return
     */
    private OrderInfo getNoPayOrderByProductId(Long productId) {
        return orderInfoMapper.selectOne(new QueryWrapper<OrderInfo>().lambda()
                        .eq(OrderInfo::getProductId, productId)
                        .eq(OrderInfo::getOrderStatus, OrderStatus.NOTPAY.getType())
                        .last(" limit 1")
                //.eq(OrderInfo::getUserId,userId)
        );
    }

}
